// SPDX-License-Identifier: GPL-2.0+

#include <common.h>
#include <pci.h>
#include <usb.h>
#include <scsi.h>
#include <ahci.h>
#include <led.h>
#include <wdt.h>
#include <asm/io.h>

#include <dm.h>
#ifdef CONFIG_DM_VIDEO
#include <video_console.h>
#include <video_font.h>
#endif
#include <mach/ls2k500.h>

#include "platform.h"

extern void user_env_fetch(void);

int __weak ls2k_board_late_init(void)
{
	return 0;
}

int checkboard(void)
{
	printf("Board: %s\n", CONFIG_LOONGSON_BOARD_NAME);

	return 0;
}

int __weak vidconsole_notice(char *notice)
{
#ifdef CONFIG_DM_VIDEO
	struct vidconsole_priv *priv;
	struct udevice *con;
	int col, row, len;

	for (uclass_first_device(UCLASS_VIDEO_CONSOLE, &con);
				con; uclass_next_device(&con)) {
		priv = dev_get_uclass_priv(con);
#if 0
		//显示bootloader版本信息
		{
		char buf[DISPLAY_OPTIONS_BANNER_LENGTH] = {0};
		display_options_get_banner(false, buf, DISPLAY_OPTIONS_BANNER_LENGTH);
		len = strcspn(buf, "\n");
		buf[len] = 0;
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 1;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, buf);
		}
#endif

		//显示操作按键提示信息
		len = strlen(notice);
		col = (priv->cols / 2) - (len / 2);
		if (col < 0)
			col = 0;
		row = priv->rows - 2;
		vidconsole_position_cursor(con, col, row);
		vidconsole_put_string(con, notice);
		vidconsole_position_cursor(con, 0, 0);
	}
#endif
	return 0;
}

static void print_notice(void)
{
	char notice[DISPLAY_OPTIONS_BANNER_LENGTH];

	sprintf(notice, "Press %s, %s", NOTICE_STR1, NOTICE_STR2);

	//显示屏控制台显示提示信息
	vidconsole_notice(notice);
	//串口控制台显示提示信息
	printf("********Notice********\n");
	printf("%s\r\n", notice);
	printf("**********************\n");
}

int board_late_init(void)
{
	if (IS_ENABLED(CONFIG_LED)) {
		led_default_state();
	}

	print_notice();
	ls2k_board_late_init();

	user_env_fetch();

	return 0;
}

#ifdef CONFIG_SCSI_AHCI
void ls2x_set_ahci(void)
{
	void __iomem *sata_phy = ioremap(LS2X_SATA_PHY_CFG, 1);
	u32 val = 0;

	/* Config SATA0 */
	//reset lane
/*	val = readl(sata_phy);
	val = val & (~(1<<3));
	writel(val, sata_phy);
	udelay(500);
	val = val | (1<<3);
	writel(val, sata_phy);
	udelay(500);
	//reset phy
	val = readl(sata_phy);
	val = val & (~(1<<2));
	writel(val, sata_phy);
	udelay(500);
	val = val | (1<<2);
	writel(val, sata_phy);
	udelay(500);*/

	val = readl(sata_phy);
	val = val & (~(1<<1));
	writel(val, sata_phy);

	/* Config SATA1 */
	sata_phy = ioremap(LS2X_SATA_PHY_CFG + 8, 1);
	//reset lane
/*	val = readl(sata_phy);
	val = val & (~(1<<3));
	writel(val, sata_phy);
	udelay(500);
	val = val | (1<<3);
	writel(val, sata_phy);
	udelay(500);
	//reset phy
	val = readl(sata_phy);
	val = val & (~(1<<2));
	writel(val, sata_phy);
	udelay(500);
	val = val | (1<<2);
	writel(val, sata_phy);
	udelay(500);*/

	val = readl(sata_phy);
	val = val & (~(1<<1));
	writel(val, sata_phy);
}
#endif

#ifdef CONFIG_SCSI_AHCI_PLAT
void scsi_init(void)
{
	void __iomem *ahci_base;

	ahci_base = 0x1f040000;
	printf("scsi ahci plat %p\n", ahci_base);
	if(!ahci_init(ahci_base))
		scsi_scan(1);
}
#endif

int misc_init_r(void)
{
	return 0;
}

int ls2x_set_usbphy(void)
{
	u32 val = 0;
	void __iomem *base;
#ifdef CONFIG_USB_XHCI_DWC3
	/*init dwc3*/
	base = ioremap(LS2X_XHCI_BASE, 1);
	//usb3.0 use internal reference clk
	val = readl(base + 0xff00);
	val = val & ~(1 << 2);
	writel(val, base + 0xff00);

	val = readl(base + 0xff04);
	val = val | 0x00040000;
	writel(val, base + 0xff04);
	val = readl(base + 0xc110);
	val = val & ~(3 << 12);
	val = val | (1 << 12);
	writel(val, base + 0xc110);
	val = readl(base + 0xc2c0);
	val = val & ~(3 << 1);
	val = val | (0 << 1);
	writel(val, base + 0xc2c0);
#endif

	base = ioremap(LS2K500_GENERAL_CFG0 + 4, 1);
	val = readl(base);
	val = val & ~(1 << 5);//关闭预读取
	val = val | (1 << 1);//usb otg模式
	val = val | (1 << 0);//usb cache一致性
	writel(val, base);

	return 0;
}

#ifdef CONFIG_BOARD_EARLY_INIT_R
extern int tlb_init_default(void);

int board_early_init_r(void)
{
	u32 val = 0;
	void __iomem *base = ioremap(LS2X_MUX_BASE, 1);

#ifdef CONFIG_MIPS
	tlb_init_default();
#endif

#ifdef CONFIG_NAND_LS1X
	//nand 复用配置 gpio70-83
	val = readl(base + 0x20);
	val = val & 0x00ffffff;
	val = val | 0x11000000;
	writel(val, base + 0x20);
	val = readl(base + 0x24);
	val = 0x55551111;
	writel(val, base + 0x24);
	val = readl(base + 0x28);
	val = val & 0xffff0000;
	val = val | 0x00005555;
	writel(val, base + 0x28);
#endif

	ls2x_set_usbphy();

#ifdef CONFIG_DM_PCI
	/*
	 * Make sure PCI bus is enumerated so that peripherals on the PCI bus
	 * can be discovered by their drivers
	 */
	pci_init();
#endif

	return 0;
}
#endif

#ifdef CONFIG_RECOVER
extern int recover(void);
#endif

#ifdef CONFIG_LAST_STAGE_INIT
int last_stage_init(void)
{
	/* start usb so that usb keyboard can be used as input device */
	if (CONFIG_IS_ENABLED(USB_KEYBOARD)) {
		usb_init();
	}

	if (CONFIG_IS_ENABLED(SCSI)) {
#ifndef CONFIG_DM_SCSI
		scsi_bus_reset(NULL);
#endif
		scsi_scan(true);
	}

	/* 上电时长按按钮3秒进入recover功能, recover优先顺序usb>mmc>sata
       按键使用CONFIG_RECOVER_GPIO_BUTTON定义的gpio */
#ifdef CONFIG_RECOVER
	recover();
#endif

	return 0;
}
#endif

void ls_pcie_fixup(int port_fix)
{
}

#if defined(CONFIG_SYS_CONSOLE_IS_IN_ENV) && \
defined(CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE)
int overwrite_console(void)
{
	env_set("stdout", "serial");

	return 0;
}
#endif

void arch_preboot_os(void)
{
#ifdef CONFIG_WATCHDOG
	wdt_stop(gd->watchdog_dev);
#endif
}
